#ifndef _LEVEL_EDITOR_H
#define _LEVEL_EDITOR_H_
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
#include "CGlobals.h"
//#include "CGameObject.h"
//#include "CSceneObject.h"
//#include "IDoUndoCommand.h"
//#include "IGUITreeCtrl.h"
//#include "PhysicalWorld.h"
#include "SMeshBuffer.h"
#include "IrrMap.h"
#include "IEventReceiver.h"
#include "ESceneNodeTypes.h"
#include "IXMLReader.h"
#include "IXMLWriter.h"
#include "triangle3d.h"
#include "quaternion.h"
#include <vector>
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
class CEffect;
class CMultiCommand;
class CGameObject;
class CSceneObject;
class IDoUndoCommand;
class CPhysicalWorld;
class CCinematicCommand;
class CCinematicThread;

namespace irr
{
	namespace gui
	{
		struct TreeNode;
		class IGUICheckBox;
	}

	namespace scene
	{
		class IMesh;
	}

	namespace io
	{
		class IWriteFile;
	}
}

#define CINEMATIC_COMMANDS_FILE	"../CINEMATIC_COMMANDS.h"
extern CGlobals* g;
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
#define KEY_EDIT_X KEY_KEY_X
#define KEY_EDIT_Y KEY_KEY_C
#define KEY_EDIT_Z KEY_KEY_Z
#define KEY_EDIT_ALL KEY_KEY_A

#define KEY_BUTTON_MOVE KEY_KEY_V
#define KEY_BUTTON_ROTATE KEY_KEY_R
#define KEY_BUTTON_SCALE KEY_KEY_S
#define KEY_BUTTON_UNSELECT KEY_KEY_Q
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
struct SGameList
{
	stringw Name;
	array<stringw> Items;
};
//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
class CLevelEditor
{
public:
	CLevelEditor();
	~CLevelEditor();
	
	//paths		
	stringc							m_SceneFileName;
	stringc							m_SceneFilePath;
	stringc							m_editorPath;

	stringc							m_buildDataPath;
	stringc							m_launchEmuPath;
	
	//list of all game objetcs 
	array<CGameObject*>				m_GameObjects;

	//save template for auto-add objects
	CGameObject* m_camCtrlPointTemplate;
	CGameObject* m_restorePointTemplate;
	CGameObject* m_effectTemplate;

	//list of all game templates 
	array<CGameObject*>				m_GameTemplates;
	//list of all scene objects 
	array<CSceneObject*>			m_SceneObjects;
	//lists 
	array<SGameList>				m_GameLists;
	array<stringc>					m_stringsFiles;
	SGameList						m_stringsList;
	//SGameList						m_myList;
	SGameList						m_skillList;
	stringc							m_musicFile;

	//selected node stuff
	vector3df						m_SelectedNodePos;
	quaternion						m_SelectedNodeRot;
	vector3df						m_SelectedNodeScale;
	//effects
	array<CEffect*>					m_Effects;
	//triangle selector for collisions
	//irr::scene::ITriangleSelector*	m_pCollisionTriSelector;
	
	typedef irr::core::array<CSceneObject*>		ObjectsList;

	typedef irr::core::map<s32, ObjectsList*>		TreeList;		// event -> list of subscribers
	typedef TreeList::Iterator						TreeListIter;

	//History commnads	
	array<IDoUndoCommand*>			m_HistoryCommands;
	int								m_nHistoryCommands;
	int								m_LastCommand;
	void							AddCommand (IDoUndoCommand* command);
	void							Redo ();
	void							Undo ();

	void							DeleteAutoSave();

	//bool							m_HasActiveZone;	
	//vector3df						m_SelectedNodeActiveZonePos;
	//vector3df						m_SelevtedNodeActiveZoneScale;
	//init stuff
	int								init();
	bool							receiveEvent(const SEvent& event);
	//load save stuff
	s32								loadScene(stringc sceneName);
	s32								loadProcessXML(irr::io::IXMLReader* xml, const irr::core::stringc& fileName);	
	void							saveScene(bool bAutoSave = false);	
	void							saveSceneObject(CSceneObject* pObj, IXMLWriter* xml);
	void							saveTreeNode(irr::gui::TreeNode* treeNode, IXMLWriter* xml,bool bAutoSave);
	void							saveRoomLinks(CSceneObject* pObj, io::IAttributes *attr);
	void							loadRoomLinks(CSceneObject* pObj, io::IAttributes *attr);
		
	void							WriteToCollisionFile(IWriteFile* pFile, ISceneNode* pNode);
	void 							GetChildren(s32 ID , s32 Level , TreeList&  );
	void							UpdateEffects( u32 time );
	void							HideLods(ISceneNode*);

	void drawFocusBoundingBox();
	void removeReferences();
	void checkReferentialIntegrity();
	void deleteSelectedNode();
	bool hasSelection() { return (bool)(m_SelectedNode != 0); };
	void setSceneFileName(stringc newFileName);
	bool hasSceneFileName() { return (m_SceneFileName.size() > 0); };
	void updateEditorCaption();
	CSceneObject* createGameObject(u32 index , s32 Id, bool bPositionObj = false , bool bLoading = false);
	CSceneObject* createGameObject(CGameObject *pTemplate , s32 Id, bool bPositionObj = false , bool bLoading = false, int parentID=-1);
	void updateTrigerControls(vector3df size);

	ISceneNode* GetParentFromSelection(stringc gameType);

	s32 getGameObjectIndexFromGameType(stringc type);


	ISceneNode* getSelectedNode() { return m_SelectedNode; };
	void setSelectedNode(ISceneNode* node);
	CSceneObject* getSceneObjectFromID(s32 id);
	void drawWaypointConnections();

	void drawCameraAreas();
	bool isCameraAreaSelected(CSceneObject* obj);
	void drawCCPDirectionLine(CSceneObject* obj);
	void drawCCPTargetOffset(CSceneObject* obj);
	void drawCameraAreaNormalRange(CSceneObject* obj, bool selected);
	void drawCameraAreaLinkLine(CSceneObject* obj);

	void drawGridWaypointConnections();
	void drawConnection(ISceneNode *pNode, int nextID, SMeshBuffer& Buffer , u16* , S3DVertex*);	
	array<stringw> getGameListArray(stringw name);
	void cloneSelectedNode();
	CSceneObject* cloneGameObject(CSceneObject *pNode);
	CSceneObject* cloneNode(ISceneNode *pNode);
	void newScene();
	void drawCameraTarget();
	void drawObjectManipulationControls ();
	void updateControls ();
	void doUpdateControls ();
	void drawObjectSelectionHandles ();
	void drawPlayCinematicInfo();
	void toggleToSelection (ISceneNode* node);
	void drawSelectedObjects ();
	void drawSelectedNode (ISceneNode *pNode);
	void copyAttributes(IAttributes* destination, IAttributes* source, int index = -1, bool bOnlyIfExists = false);
	void snapNode();
	int writeConfigOptions();
	void saveEffectPreset();
	void saveGameTemplates();

	void createEffectNameInput(bool bClone = false);
	void createPSNameInput();
	void createTemplateNameInput();

	void showHideItems(const char*);
	void showHideItems(const char*, bool show);

	void switchSelAllCameras();

	bool RayCollision(const vector3df &inRayPoint, 
							const vector3df &inRayDirection, 
									vector3df &outCollisionPoint, bool bCulling = false);

	int GetRoomID(ISceneNode *forSceneNode);

	void update( );

	//ISceneUserDataSerializer functions:
	
	IAttributes* createUserData (irr::scene::ISceneNode *forSceneNode);
	//void OnReadUserData (irr::scene::ISceneNode *forSceneNode, irr::io::IAttributes *userData);

	//int getNextWaypoint (int waypoint);
	//int getPrevWaypoint (int waypoint);

	void addObjectToTreeView(CSceneObject* obj, int parentNodeID=-1);

	bool m_clearHistoryOnNewCommand;

	array<int> m_menuIds;

	array<int> m_onScreenIds;
	array<position2di> m_onScreenPositions;
	array<int> m_selectedIndices;

	bool isPointOverGUI(position2di pos);

	array<CSceneObject*> m_selection;

	bool m_SaveLoadProgressDraw;
	s32 m_SaveLoadProgress;

	bool IsModified() { return m_bModified; };
	
	void		updateMeshFromNodes( ISceneNode* node , vector3df diff);

	IMesh*		createMesh();
	IMesh*		createMeshWithSize( vector3df size );
	void		updateMeshWithSize( IMesh *mesh , vector3df size );

	IMesh*		createTriPrismMesh();
	IMesh*		createTriPrismMeshWithParams( f32 size, f32 angle );
	void		updateTriPrismMeshWithParams( IMesh *mesh , f32 size, f32 angle  );
	
	void		SetObjectEffectType(ISceneNode* node, stringw effectType);
	void		SetEnemyType(ISceneNode* node, int type);

	void SetEffectVisible(int effect, bool bVisible);

	void ReAddEffectsToScene();

	stringw GetFilterFromName(stringw name);

	bool m_bPositionObjOnInsert;
	CGameObject*	m_pInsertObjTemplate;

	bool m_bLinkRoomMode;
	int m_bLinkRoomModeStep;
	ISceneNode *m_pLinkFrom;
	short m_linkFromIndex;
	ISceneNode *m_pLinkTo;
	short m_linkToIndex;
	void AddLinkHelperNodes(ISceneNode* pRoot);
	ISceneNode* GetRoomFromHelper(ISceneNode *pHelper);
	void ShowRoomHelpers(ISceneNode* pRoom, bool bShow);
	void LinkRoomFromTo(ISceneNode *pLinkFrom, short linkFromIndex, ISceneNode *pLinkTo, short linkToIndex, CMultiCommand *command, bool bRecursive = false);
	ISceneNode* GetLinkNodeFromName(ISceneNode *pStartNode, const stringc& name);

	void RecalculateCollisions();

	void		activateCamera();	

	void AddInsertCommand (CSceneObject* object);

	void GetAbsoluteBoundingBox(ISceneNode* pNode, aabbox3df& box);
	void CheckSpawnPoints(CSceneObject* object , bool);

	void PrintRelativeTrans(ISceneNode* pNode);

	ISceneNode*	GetParentRoom(ISceneNode* pNode);

	ISceneNode* GetRoomRefNode(ISceneNode* pRoomNode);
	CPhysicalWorld* m_physicalWorld;

	bool LoadListFromFile(stringc fileName, SGameList& list);

	line3df m_screenRay;
	triangle3df m_collTriangle;	
	
	//correct camera area if a camera area or its sub cam ctrl point is selected
	void CorrectSelectedCameraArea();

	void OnOffCameraModeCtrlPoint(bool on, bool cancelEdit=false);

	void CopySelectedCameraPoint(bool doCopy=false);

	void ChangeCamera(bool begin, bool cancelEdit=false, CCinematicCommand* cmd = NULL, int nowTime=0);

	//do some preprocess work: find if two ctrl points of an old CA selected, so place the new CA according to the two points
	void preprocessCreateCameraArea(CGameObject* objTemplate);

	void MoveObject(bool begin,CSceneObject* pNode= NULL,CCinematicCommand* cmd = NULL, int nowTime=0, bool posOnly=false);

	void OnOffPlayCinematic(bool on, bool fromCurrentAndPause=false);
	void PausePlayCinematic();
	void StepPlayCinematic();
	void PlayCinematic(int time, bool backgroundRun=false);
	bool IsPlayingCinematic() { return m_isPlayingCinematic; }
	bool IsCinematicPause() { return m_isCinematicPause;}
	bool IsCinematicStepOneFrame() { return m_isCinematicStepOneFrame;}

	void OnOffCamCtrlPointSetOffset(bool on, CSceneObject* pCamCtrlPoint);
	void SnapCamCtrlPointTargetToEdge(CSceneObject* pCamCtrlPoint);

	void PlayCinematicToTime(int destTime);

private:

	void GetColladaBoundingBox(ISceneNode* pNode, aabbox3df& box, stringc dummy);

	void insertChildren (map<CSceneObject*, bool>* isChild, CSceneObject* obj);

	bool m_KeysDown[KEY_KEY_CODES_COUNT];

	void RemoveCommand ();

	ISceneNode* m_LastFocusedNode;
	ISceneNode* m_SelectedNode;
	position2di m_SelectedMousePos;
		

	int m_lastUniqueId;
	int m_minId;
	int m_maxId;
		
	ESCENE_NODE_TYPE getObjectType(stringw typeStr);

	int readGameObjects();

	int readGameTemplates();

	int readConfigOptions();
	
	int readEffectsPreset();

	void AddEffectToTypeList();

	bool isPointOverGUIElement(position2di pos, IGUIElement* e);
	int getUniqueSceneObjectID( int oldId );
	
	ISceneNode* createColladaMesh(CGameObject* objTemplate);
	ISceneNode* createSkyBox(CGameObject* objTemplate);
	ISceneNode* createGameTrigger(CGameObject* objTemplate);
	
	ISceneNode* createCameraArea(CGameObject* objTemplate);	
	void autoCreateCamCtrlPoints(CSceneObject* cameraAreaObj);
	void autoCreateWayPoint(CSceneObject* pTriggerRestoreObj);

	ISceneNode* createCameraDirCtl(CGameObject* objTemplate);
	ISceneNode* createEmptySceneNode(CGameObject* objTemplate);

		
	void editObject(bool axisX, bool axisY, bool axisZ, position2di mousePos);

	void SetPositionInFrontOfTheCamera(ISceneNode* node);

	void FocusNodeForSelection(line3df ray, position2di mousePos);

	bool m_mouseDraggedWithCtrl;
	position2di m_mouseDragStart;

	bool m_mouseClicked;
	position2di m_mouseClickPos;

	bool m_updateControls;
	bool m_bModified;

	s32	m_LastCreatedObjID;

	//used for create camera area nearby
	// [0] pos1, [1] pos2, [2] dirVec, 
	// [3] point1 cam dir, [4] point2 cam dir, [5] X: point1 cam dis Y: point2 cam dis
	// [6] target offset1 [7] target offset2 [8]  X:point 1 target height off Y:point 2 target height
	std::vector<irr::core::vector3df> m_caRefVecs; 

	bool m_isPlayingCinematic;
	bool m_isPlayingCinematicEnd;
	bool m_isCinematicPause;
	bool m_isCinematicStepOneFrame;//step 50ms

	int m_cnm_lastTime;
	int m_cnm_currentTime;	
public:
	std::vector<CSceneObject*> m_cnm_objUpdateList;

private:

	bool execInterpCmd(CCinematicThread* thread, int currentTime, const stringw& cmdName, int& lastExecKey);
	bool execCmds(CCinematicThread* thread, int currentTime, bool backgroundRun);

	bool m_isMovingObject;
	CSceneObject* m_cnm_obj_moved;
	CCinematicCommand* m_cnm_objmove_cmd;	

	irr::core::vector3df m_savedCamCtrlPointPos; //for set offset
		
	irr::gui::IGUICheckBox *m_ckb_cpycam_pos, *m_ckb_cpycam_cam, *m_ckb_cpycam_target;
};

#endif
